import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib.image import imread
%matplotlib inline
pd.set_option('display.max_columns', 100)
import missingno as msno
import plotly.graph_objects as go
import plotly.express as px
from plotly.subplots import make_subplots
import plotly
import cv2
import os
from os import path
from PIL import Image
from sklearn.decomposition import LatentDirichletAllocation
from sklearn.manifold import TSNE
from sklearn.decomposition import PCA
from sklearn.cluster import KMeans
from sklearn import metrics
from tensorflow.keras.layers import *
from tensorflow.keras.models import Model
from tensorflow.keras import optimizers
from keras.applications.vgg16 import VGG16, decode_predictions
from keras.preprocessing.image import load_img, img_to_array, ImageDataGenerator
from keras.applications.vgg16 import preprocess_input
import time
import shutil
import warnings
warnings.filterwarnings('ignore')
data = pd.read_csv("Flipkart/data_cleaned.csv")
print("Le jeu de données flipkart_com-ecommerce_sample contient %d lignes et %d colonnes." % (data.shape[0], data.shape[1]))
data.head()
image_path = [data['image'][i] for i in range(data.shape[0])]
print(len(image_path))
dir_name = 'Flipkart/Images/'
# Exemple sur une image
image_exemple = data['image'].loc[5]
file_path = os.path.join(dir_name, image_exemple)
# Afficher l'image
img_exemple = cv2.imread(file_path)
plt.imshow(img_exemple)
# Exemple sur une image
image_exemple = data['image'].loc[80]
file_path = os.path.join(dir_name, image_exemple)
# Afficher l'image
img_exemple = cv2.imread(file_path)
plt.imshow(img_exemple)
for name in data['product_category_1'].unique():
plt.figure(figsize=(10,10))
print(name)
for i in range(5):
plt.subplot(4, 5, i+1)
filename = dir_name + list(data[data["product_category_1"] == name]['image'])[i+31]
image = imread(filename)
plt.imshow(image)
plt.axis('off')
plt.show()
L'algorithme SIFT permet d'extraire des features (ou points d'intérêt) de l'image et de calculer leurs descripteurs. Il permets de détecter et identifier les éléments similaires entre différentes images.
image_exemple = data['image'].loc[8]
file_path = os.path.join(dir_name, image_exemple)
sift = cv2.SIFT_create(500)
image = cv2.imread(file_path)
print('Image originale')
plt.imshow(image)
plt.axis("off")
plt.show()
image = cv2.imread(file_path, 0) # convert in gray
print('Image gris')
plt.imshow(image, cmap='gray')
plt.axis("off")
plt.show()
image = cv2.GaussianBlur(image, (5,5), cv2.BORDER_DEFAULT)
print('Image flou')
plt.imshow(image, cmap='gray')
plt.axis("off")
plt.show()
image = cv2.resize(image, (255, 255), interpolation=cv2.INTER_AREA)
print('Image redimensionné')
plt.imshow(image, cmap='gray')
plt.axis("off")
plt.show()
image = cv2.equalizeHist(image) # equalize image histogram
print('Image avec contrast')
plt.imshow(image, cmap='gray')
plt.axis("off")
plt.show()
clahe = cv2.createCLAHE() # Equalization by CLAHE
image = clahe.apply(image)
print('Contraste adapté et egalisation des histogrammes')
plt.imshow(image, cmap='gray')
plt.axis("off")
plt.show()
kp, des = sift.detectAndCompute(image, None)
img=cv2.drawKeypoints(image,kp,image)
print('Image prétraitée et descripteurs')
plt.imshow(img)
plt.axis("off")
plt.show()
print("Descripteurs : ", des.shape)
print()
print(des)
image_exemple1 = data['image'].loc[7]
image_exemple2 = data['image'].loc[15]
file_path1 = os.path.join(dir_name, image_exemple1)
file_path2 = os.path.join(dir_name, image_exemple2)
# read images
img1 = cv2.imread(file_path1)
img2 = cv2.imread(file_path2)
img1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
img2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)
figure, ax = plt.subplots(1, 2, figsize=(16, 8))
ax[0].imshow(img1, cmap='gray')
ax[1].imshow(img2, cmap='gray')
#sift
sift = cv2.SIFT_create()
keypoints_1, descriptors_1 = sift.detectAndCompute(img1,None)
keypoints_2, descriptors_2 = sift.detectAndCompute(img2,None)
#feature matching
bf = cv2.BFMatcher(cv2.NORM_L1, crossCheck=True)
matches = bf.match(descriptors_1,descriptors_2)
matches = sorted(matches, key = lambda x:x.distance)
img3 = cv2.drawMatches(img1, keypoints_1, img2, keypoints_2, matches[:50], img2, flags=2)
plt.imshow(img3),plt.show()
print("Nombre de features communs aux deux images: {} features".format(len(matches)))
# identification of key points and associated descriptors
sift_keypoints = []
sift = cv2.SIFT_create()
clahe = cv2.createCLAHE()
for image_num in range(len(image_path)) :
if image_num%100 == 0 : print(image_num)
image = cv2.imread(dir_name+image_path[image_num],0) # convert in gray
image = cv2.GaussianBlur(image, (5,5), cv2.BORDER_DEFAULT)
image = cv2.resize(image, (255, 255), interpolation=cv2.INTER_AREA)
image = cv2.equalizeHist(image) # equalize image histogram
res = clahe.apply(image)
kp, des = sift.detectAndCompute(res, None)
sift_keypoints.append(des)
sift_keypoints_by_img = np.asarray(sift_keypoints, dtype='object')
sift_keypoints_all = np.concatenate(sift_keypoints_by_img, axis=0)
print("Descripteurs : ", sift_keypoints_all.shape)
Un descripteur est un vecteur qui décrit le voisinage de la feature à laquelle il est associé. Il est utilisé pour repérer les paires de features qui se ressemblent le plus dans deux images. Pour faciliter cette étape de matching, le descripteur doit présenter de nombreuses propriétés d'invariance (rotation, échelle, illumination).
list_ari = []
def plot_kmeans_tsne(reduction, title, filename, colname):
kmeans_tsne = KMeans(n_clusters=7, n_init=50, max_iter=200,init='k-means++', random_state=42).fit(reduction)
labels_tsne = kmeans_tsne.labels_
cl_tsne = pd.concat([reduction,pd.DataFrame({'tsne_clusters':labels_tsne})],axis=1)
data[f'cluster {colname}'] = labels_tsne
categories_predict = data[f'cluster {colname}']
categories_true = data['product_category_1']
adjusted_rand = metrics.adjusted_rand_score(categories_true, categories_predict)
list_ari.append(adjusted_rand)
print("\033[1mAdjusted Rand Index: %0.3f\033[0m" % adjusted_rand)
fig = px.scatter(data, x=cl_tsne.iloc[:,0], y = cl_tsne.iloc[:,1], color=categories_true, title=f"Représentation selon les vraies classes {title}")
fig1 = px.scatter(data, x = cl_tsne.iloc[:,0],y = cl_tsne.iloc[:,1], color=categories_predict, title = f"Représentation selon les clusters {title}")
plotly.offline.plot(fig, filename=f'plots/{filename}.html')
plotly.offline.plot(fig1, filename=f'plots/{filename}_cluster.html')
return fig.show(), fig1.show()
from yellowbrick.cluster import KElbowVisualizer
# Instantiate the clustering model and visualizer
model = KMeans()
visualizer = KElbowVisualizer(
model, k=(2,10), timings=True
)
visualizer.fit(sift_keypoints_all) # Fit the data to the visualizer
visualizer.show() # Finalize and render the figure
from sklearn import cluster, metrics
k = int(round(np.sqrt(len(sift_keypoints_all)),0))
print("Nombre de clusters estimés : ", k)
print("Création de",k, "clusters de descripteurs ...")
# Clustering
kmeans = cluster.MiniBatchKMeans(n_clusters=k, init_size=3*k,
batch_size=2000, random_state=0)
kmeans.fit(sift_keypoints_all)
def build_histogram(kmeans, des, image_num):
res = kmeans.predict(des)
hist = np.zeros(len(kmeans.cluster_centers_))
nb_des=len(des)
if nb_des==0 : print("problème histogramme image : ", image_num)
for i in res:
hist[i] += 1.0/nb_des
return hist
# Creation of a matrix of histograms
hist_vectors=[]
for i, image_desc in enumerate(sift_keypoints_by_img) :
hist = build_histogram(kmeans, image_desc, i) #calculates the histogram
hist_vectors.append(hist) #histogram is the feature vector
im_features_sift = np.asarray(hist_vectors)
def pca(vector):
pca = PCA(n_components=0.95)
ft_pca = pca.fit_transform(vector)
return ft_pca
pca_sift = pca(im_features_sift)
tsne = TSNE(n_components=2, verbose=1, perplexity=80,n_iter=5000, learning_rate=200, random_state=42)
X_tsne_sift = tsne.fit_transform(pca_sift)
df_sift = pd.DataFrame(X_tsne_sift[:,0:2], columns=['tsne1', 'tsne2'])
print(df_sift.shape)
plot_kmeans_tsne(df_sift, "Clusters des descripteurs", "kmeans_sift", "sift")
Le graphique montre que avec sift seulement, les categories sont très mal classifié
# Analyse des différentes catégories dans les labels
index_tot = [data[data['cluster sift'] == x].index
for x in data['cluster sift'].value_counts().index]
plt.figure(figsize=(20, 20))
for x in range(len(index_tot)):
order = data.loc[index_tot[x], 'product_category_1'].value_counts()
order_hue = order.index
plt.subplot(4, len(index_tot)/3, x+1)
sns.countplot(y=data.loc[index_tot[x], 'product_category_1'],
order=order_hue,
palette='Blues_r')
plt.title(f"Cluster {x}", fontsize=20)
pathTrain = r'Flipkart/Images/Train/'
pathTest = r'Flipkart/Images/Test/'
pathValidation = r'Flipkart/Images/Validation/'
if os.path.exists(pathTrain) == False:
os.mkdir(pathTrain)
if os.path.exists(pathTest) == False:
os.mkdir(pathTest)
if os.path.exists(pathValidation) == False:
os.mkdir(pathValidation)
data['IDcatégorie'] = data['product_category_1'].copy()
map_categorie = {'Baby Care ': 0,
'Beauty and Personal Care ': 1,
'Computers ': 2,
'Home Decor & Festive Needs ': 3,
'Home Furnishing ': 4,
'Kitchen & Dining ': 5,
'Watches ': 6
}
data['IDcatégorie'] = data['IDcatégorie'].map(map_categorie)
data.product_category_1.unique().tolist()
data.IDcatégorie.unique().tolist()
X = data[['uniq_id', 'image', 'product_category_1', 'IDcatégorie']].copy()
Y = data[['uniq_id', 'product_category_1', 'IDcatégorie']]
listeCatégorie = ['Home Furnishing ', 'Baby Care ', 'Watches ',
'Home Decor & Festive Needs ', 'Kitchen & Dining ',
'Beauty and Personal Care ', 'Computers ']
Nous allons prendre 70% des images pour l'entraînement et 30% pour le test, soit 105 images pour l'entraînement et 45 pour le test et la validation.
for catégorie in listeCatégorie:
path = pathTrain + catégorie +'/'
if os.path.exists(path) == False:
os.mkdir(pathTrain + catégorie +'/')
listeImage = X[X['product_category_1'] == catégorie]['image'].values.tolist()[0:105]
else:
listeImage = X[X['product_category_1'] == catégorie]['image'].values.tolist()[0:105]
for image in listeImage:
shutil.copy(r'Flipkart/Images/' + image, pathTrain+catégorie+'/'+image)
for catégorie in listeCatégorie:
path = pathTest+ catégorie +'/'
if os.path.exists(path) == False:
os.mkdir(pathTest + catégorie +'/')
listeImage = X[X['product_category_1'] == catégorie]['image'].values.tolist()[105:150]
else:
listeImage = X[X['product_category_1'] == catégorie]['image'].values.tolist()[105:150]
for image in listeImage:
shutil.copy(r'Flipkart/Images/' + image, pathTest +catégorie+'/'+image)
for catégorie in listeCatégorie:
path = pathValidation + catégorie +'/'
if os.path.exists(path) == False:
os.mkdir(pathValidation + catégorie +'/')
listeImage = X[X['product_category_1'] == catégorie]['image'].values.tolist()[60:105]
else:
listeImage = X[X['product_category_1'] == catégorie]['image'].values.tolist()[60:105]
for image in listeImage:
shutil.copy(r'Flipkart/Images/' + image, pathValidation +catégorie+'/'+image)
# Load the normalized images
train_datagen = ImageDataGenerator(rescale = 1./255)
validation_datagen = ImageDataGenerator(rescale = 1./255)
Test_datagen = ImageDataGenerator(rescale = 1./255)
# Change the batchsize
train_batchsize = 100
val_batchsize = 10
target_size=(224, 224)
# Data generator for training data
train_generator = train_datagen.flow_from_directory(
pathTrain,
target_size=target_size,
batch_size=train_batchsize,
class_mode='categorical')
# Data generator for test data
validation_generator = validation_datagen.flow_from_directory(
pathValidation,
target_size=(224, 224),
batch_size=val_batchsize,
class_mode='categorical',
shuffle=True)
# Data generator for validation data
test_generator = Test_datagen.flow_from_directory(
pathTest,
target_size = (224, 224),
batch_size = 1,
class_mode = None,
shuffle = False)
Version du réseau de neurones convolutif très connu appelé VGG-Net.
IMGSIZE = 224 # Taille de l'image en input
EPOCH = 22 # nombre d'epoch
BATCH_SIZE = 16 # traitement par batch d'images avant la descente de gradient
FREEZE_LAYERS = 15 # pour un VGG16 freeze de réapprentissage de certaines couches
TRAIN = True # Entrainement ou utilisation d'un réseau déjà entrainé
img = load_img(r'Flipkart/Images/3e2b2a04696f7d83a7835e9894d79df7.jpg') # Charger l'image
plt.imshow(img)
data.loc[data['image'] == '3e2b2a04696f7d83a7835e9894d79df7.jpg']
Ce lit appartient à la catégorie Baby Care
img = img.resize((224, 224))
img = img_to_array(img) # Convertir en tableau numpy
img = img.reshape((1, img.shape[0], img.shape[1], img.shape[2])) # Créer la collection d'images (un seul échantillon)
img = preprocess_input(img) # Prétraiter l'image comme le veut VGG-16
model = VGG16()
y = model.predict(img)
print('Top 5 :', decode_predictions(y, top=5)[0])
VGG16 a bien reconnu que cette image contenait un oreiller un drap, une commode qui est juste. Mais vu que le jeu de données contient 7 classes spécifiques on va donc utiliser le transfer learning.
train_image_files = pathTrain
test_image_files = pathTest
IMSIZE = 224
def create_model():
vgg = VGG16(input_shape=(IMSIZE, IMSIZE, 3), weights='imagenet', include_top=False)
# Freeze existing VGG already trained weights
for layer in vgg.layers:
layer.trainable = False
# Charger VGG-16 pré-entraîné sur ImageNet et sans les couches fully-connected
model = VGG16(weights="imagenet", include_top=False, input_shape=(224, 224, 3))
# Récupérer la sortie de ce réseau
x = model.output
x = GlobalMaxPooling2D()(x)
x = Dense(4096,activation='relu')(x)
x = Dense(4096,activation='relu')(x)
# Ajouter la nouvelle couche fully-connected pour la classification de nos 7 classes
predictions = Dense(7, activation='softmax')(x)
# Définir le nouveau modèle
model = Model(inputs=model.input, outputs=predictions)
# Compiler le modèle
model.compile(loss="categorical_crossentropy",
optimizer=optimizers.SGD(learning_rate=0.0001, momentum=0.9),
metrics=['accuracy'])
model.summary()
return model
mymodel = create_model()
history = mymodel.fit(
train_generator,
steps_per_epoch = train_generator.samples/train_generator.batch_size,
epochs = 20,
validation_data = validation_generator,
validation_steps = validation_generator.samples/validation_generator.batch_size, verbose=1
)
# Utility function for plotting of the model results
def visualize_results(history):
# Plot the accuracy and loss curves
acc = history.history["accuracy"]
val_acc = history.history['val_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']
epochs = range(len(acc))
plt.plot(epochs, acc, 'b', label='Training acc')
plt.plot(epochs, val_acc, 'r', label='Validation acc')
plt.title('Training and validation accuracy')
plt.legend()
plt.figure()
plt.plot(epochs, loss, 'b', label='Training loss')
plt.plot(epochs, val_loss, 'r', label='Validation loss')
plt.title('Training and validation loss')
plt.legend()
plt.show()
# Run the function to illustrate accuracy and loss
visualize_results(history)
Maintenant que notre modèle est entrainé nous allons pouvoir l'évaluer et lui demander de faire les prédictions sur nos images de tests
mymodel.evaluate_generator(generator = validation_generator)
mymodel.evaluate_generator(generator = train_generator)
Nous pouvons voir un taux de bonne classification supérieur à 94%
# Affichage des classes du jeu d'entrainement
train_generator.class_indices
# Génération des prédictions
test_generator.reset()
pred = mymodel.predict_generator(test_generator, steps = test_generator.samples/test_generator.batch_size, verbose = 1)
predicted_classes = np.argmax(pred, axis=1)
labels = train_generator.class_indices
labels = dict((v,k) for k,v in labels.items())
predictions = [labels[k] for k in predicted_classes]
# Retrieve a batch of images from the test set
filenamesTest = test_generator.filepaths
for i in range(10):
ax = plt.subplot(2, 5, i+1)
print(filenamesTest[i+30])
img = load_img(filenamesTest[i+30]) # Charger l'image
plt.imshow(img)
plt.title([predictions[i]])
plt.axis("off")
On peut voir qu'il y en a des produits qui ont été mal classifié mais c'est peut être dû à la similarité entre les produits des divers catégories.
# identification of key points and associated descriptors
import time, cv2
path = "Flipkart/Images/"
list_photos = data['image'].values.tolist()
vgg_keypoints = []
temps1=time.time()
for image_num in range(len(list_photos)) :
if image_num%50 == 0 : print(image_num)
# load an image from file
image = load_img(path+list_photos[image_num], target_size=(224, 224))
# convert the image pixels to a numpy array
image = img_to_array(image)
#reduction bruit
image = cv2.GaussianBlur(image, (5, 5), 0)
# reshape data for the model
image = image.reshape((1, image.shape[0], image.shape[1], image.shape[2]))
# prepare the image for the VGG model
image = preprocess_input(image)
features = mymodel.predict(image)
vgg_keypoints.append(features)
vgg_keypoints_by_img = np.asarray(vgg_keypoints)
vgg_keypoints_all = np.concatenate(vgg_keypoints_by_img, axis=0)
print()
print("Nombre de descripteurs VGG : ", vgg_keypoints_all.shape)
duration1=time.time()-temps1
print("temps de traitement VGG descriptor : ", "%15.2f" % duration1, "secondes")
pca_vgg_lem = pca(vgg_keypoints_all)
X_tsne_vgg = tsne.fit_transform(pca_vgg_lem)
df_tsne_vgg = pd.DataFrame(X_tsne_vgg, columns=['tsne1', 'tsne2'])
print(df_tsne_vgg.shape)
plot_kmeans_tsne(df_tsne_vgg, "Clusters des descripteurs VGG", "kmeans_vgg", "vgg")
# Analyse des différentes catégories dans les labels
index_tot = [data[data['cluster vgg'] == x].index
for x in data['cluster vgg'].value_counts().index]
plt.figure(figsize=(20, 20))
for x in range(len(index_tot)):
order = data.loc[index_tot[x], 'product_category_1'].value_counts()
order_hue = order.index
plt.subplot(4, len(index_tot)/3, x+1)
sns.countplot(y=data.loc[index_tot[x], 'product_category_1'],
order=order_hue,
palette='Blues_r')
plt.title(f"Cluster {x}", fontsize=20)
df_ari=pd.DataFrame([list_ari]
,columns=['sift','vgg'],
index=['ARI_SCORE'])
df_ari.T.round(2).plot(kind="bar",figsize=(10,6))
plt.xlabel("Model")
plt.ylabel("ARI Score")
df1 = pd.read_csv("Flipkart/ari.csv", index_col=0)
df = df1.join(df_ari, how="inner")
df.T.round(2).plot(kind="bar",figsize=(10,6))
plt.xlabel("Model")
plt.ylabel("ARI Score")
df.to_csv("Flipkart/ari_im.csv")
im_features_df = pd.DataFrame(im_features_sift)
im_features_df.to_csv('Flipkart/im_features_sift.csv')
im_features_df.shape
im_vgg_features_df = pd.DataFrame(vgg_keypoints_all)
im_vgg_features_df.to_csv('Flipkart/im_features_vgg.csv')
im_vgg_features_df.shape
Le modèle Kmeans avec le CNN donne le meilleur résultat avec 0.470